/*
# Copyright (c) 2024 Huawei Device Co., Ltd.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
*/

#include "ohos_video_receiver.h"
#include <multimedia/image_framework/image/image_receiver_native.h>
#include <ohcamera/camera.h>
#include <ohcamera/camera_manager.h>
#include <string>

#define THIS_FILE "ohos_video_receiver.cpp"
std::map<OH_ImageReceiverNative *, VideoFrameReceiver *> VideoFrameReceiver::receiverMap_;

VideoFrameReceiver *VideoFrameReceiver::Create(FrameCallBack observer) {
	return new VideoFrameReceiver(observer);
}

VideoFrameReceiver::VideoFrameReceiver(FrameCallBack observer) : callback_(observer) {
}
VideoFrameReceiver::~VideoFrameReceiver() {
	ms_message("%s method:%s: %d", THIS_FILE, __FUNCTION__, __LINE__);
	destroy();
}

void VideoFrameReceiver::OnImageReceiverCallback_1(OH_ImageReceiverNative *receiver) {

	auto it = receiverMap_.find(receiver);
	if (it == receiverMap_.end()) {
		return;
	}
	it->second->OnImageReceiverCallback();
}

void VideoFrameReceiver::OnImageReceiverCallback() {
	OH_ImageNative *image;
	Image_ErrorCode ret = OH_ImageReceiverNative_ReadLatestImage(receiver_, &image);
	if (ret != IMAGE_SUCCESS) {
		ms_message("%s method:%s: %d", THIS_FILE, __FUNCTION__, __LINE__);
		return;
	}
	Image_Size imageSize;
	ret = OH_ImageNative_GetImageSize(image, &imageSize);
	if (ret != IMAGE_SUCCESS) {
		OH_ImageNative_Release(image);
		ms_message("%s method:%s: %d", THIS_FILE, __FUNCTION__, __LINE__);
		return;
	}

	size_t typeSize;
	ret = OH_ImageNative_GetComponentTypes(image, nullptr, &typeSize);
	if (ret != IMAGE_SUCCESS || typeSize <= 0) {
		OH_ImageNative_Release(image);
		ms_message("%s method:%s: %d", THIS_FILE, __FUNCTION__, __LINE__);
		return;
	}
	uint32_t *types = new uint32_t[typeSize];
	ret = OH_ImageNative_GetComponentTypes(image, &types, &typeSize);

	if (ret != IMAGE_SUCCESS) {
		OH_ImageNative_Release(image);
		delete[] types;
		ms_message("%s method:%s: %d", THIS_FILE, __FUNCTION__, __LINE__);
		return;
	}

	int32_t rowStride;
	ret = OH_ImageNative_GetRowStride(image, types[0], &rowStride);
	if (ret != IMAGE_SUCCESS) {
		OH_ImageNative_Release(image);
		delete[] types;
		ms_message("%s method:%s: %d", THIS_FILE, __FUNCTION__, __LINE__);
		return;
	}
	ms_message("%s:%d rowStride: %d", __FUNCTION__, __LINE__, rowStride);

	int32_t pixelStride;
	ret = OH_ImageNative_GetPixelStride(image, types[0], &pixelStride);
	if (ret != IMAGE_SUCCESS) {
		OH_ImageNative_Release(image);
		delete[] types;
		ms_message("%s method:%s: %d", THIS_FILE, __FUNCTION__, __LINE__);
		return;
	}
	ms_message("%s:%d pixelStride: %d", __FUNCTION__, __LINE__, pixelStride);

	OH_NativeBuffer *buffer;
	size_t bufferSize;
	ret = OH_ImageNative_GetByteBuffer(image, types[0], &buffer);
	if (ret != IMAGE_SUCCESS) {

		OH_ImageNative_Release(image);
		delete[] types;
		ms_message("%s method:%s: %d", THIS_FILE, __FUNCTION__, __LINE__);
		return;
	}

	ret = OH_ImageNative_GetBufferSize(image, types[0], &bufferSize);
	if (ret != IMAGE_SUCCESS) {
		OH_ImageNative_Release(image);
		delete[] types;
		ms_message("%s method:%s: %d", THIS_FILE, __FUNCTION__, __LINE__);
		return;
	}

	delete[] types;
	uint8_t *srcBuffer = nullptr;
	int32_t retInt = OH_NativeBuffer_Map(buffer, (void **)&srcBuffer);
	if (retInt != 0) {
		OH_ImageNative_Release(image);
		ms_message("%s method:%s: %d", THIS_FILE, __FUNCTION__, __LINE__);
		return;
	}
	if (rowStride != imageSize.width) {
		// 传给其他支持stride的接口处理，或去除stride数据
		// 去除stride数据示例:将byteBuffer中的数据去除stride，拷贝得到新的dstBuffer数据
		size_t dstBufferSize = imageSize.width * imageSize.height * 1.5; // 相机预览流返回NV21格式
		std::unique_ptr<uint8_t[]> dstBuffer = std::make_unique<uint8_t[]>(dstBufferSize);
		uint8_t *dstPtr = dstBuffer.get();
		for (int j = 0; j < imageSize.height * 1.5; j++) {
			memcpy(dstPtr, srcBuffer, imageSize.width);
			dstPtr += imageSize.width;
			srcBuffer += rowStride;
		}
	}

	ms_message("%s:%d imageSize.height: %d, imageSize.width: %d", __FUNCTION__, __LINE__, imageSize.height,
	           imageSize.width);
	callback_.onFrameCallBack(steam, srcBuffer, bufferSize);
	OH_NativeBuffer_Unmap(buffer); // 释放buffer,保证bufferQueue正常轮转
	OH_ImageNative_Release(image);
}

void VideoFrameReceiver::CreateVideoFrameReceiver() {
	ms_message("%s method:%s: %d", THIS_FILE, __FUNCTION__, __LINE__);
	OH_ImageReceiverOptions *options = nullptr;
	OH_ImageReceiverNative *imageReceiver = nullptr;
	int32_t capacity = 8; // BufferQueue里最大Image数量，推荐填写8
	Image_ErrorCode errCode = OH_ImageReceiverOptions_Create(&options);
	if (errCode != IMAGE_SUCCESS) {
		ms_message("%s method:%s: %d", THIS_FILE, __FUNCTION__, __LINE__);
		return;
	}
	errCode = OH_ImageReceiverOptions_SetSize(options, {static_cast<uint32_t>(width_), static_cast<uint32_t>(height_)});
	if (errCode != IMAGE_SUCCESS) {
		ms_message("%s method:%s: %d", THIS_FILE, __FUNCTION__, __LINE__);
		return;
	}
	errCode = OH_ImageReceiverOptions_SetCapacity(options, capacity);
	if (errCode != IMAGE_SUCCESS) {
		ms_message("%s method:%s: %d", THIS_FILE, __FUNCTION__, __LINE__);
		return;
	}
	errCode = OH_ImageReceiverNative_Create(options, &imageReceiver);
	if (errCode != IMAGE_SUCCESS) {
		ms_message("%s method:%s: %d", THIS_FILE, __FUNCTION__, __LINE__);
		return;
	}

	errCode = OH_ImageReceiverNative_GetReceivingSurfaceId(imageReceiver, &surfaceId_);
	if (errCode != IMAGE_SUCCESS) {
		ms_message("%s method:%s: %d", THIS_FILE, __FUNCTION__, __LINE__);
		return;
	}
	receiverMap_[imageReceiver] = this;
	receiver_ = imageReceiver;
	errCode = OH_ImageReceiverNative_On(imageReceiver, VideoFrameReceiver::OnImageReceiverCallback_1);
	if (errCode != IMAGE_SUCCESS) {
		ms_message("%s method:%s: %d", THIS_FILE, __FUNCTION__, __LINE__);
		return;
	}
	errCode = OH_ImageReceiverOptions_Release(options);
	if (errCode != IMAGE_SUCCESS) {
		ms_message("%s method:%s: %d", THIS_FILE, __FUNCTION__, __LINE__);
		return;
	}
}

void VideoFrameReceiver::InitCamera(int32_t width, int32_t height) {
	width_ = width;
	height_ = height;
	ms_message("%s method:%s: %d", THIS_FILE, __FUNCTION__, __LINE__);
	CreateVideoFrameReceiver();
	Camera_ErrorCode ret = OH_Camera_GetCameraManager(&camera_manager_);
	if (camera_manager_ == nullptr || ret != CAMERA_OK) {
		ms_message("%s method:%s: %d", THIS_FILE, __FUNCTION__, __LINE__);
		return;
	}
	ret = OH_CameraManager_GetSupportedCameras(camera_manager_, &cameras_, &cameras_size_);
	if (cameras_ == nullptr || ret != CAMERA_OK || cameras_size_ <= 0) {
		ms_message("%s method:%s: %d", THIS_FILE, __FUNCTION__, __LINE__);
		return;
	}
	ms_message("%s method:%s: %d", THIS_FILE, __FUNCTION__, __LINE__);
}

int VideoFrameReceiver::getCameraCount() {
	ms_message("%s method:%s: %d", THIS_FILE, __FUNCTION__, __LINE__);
	return cameras_size_;
}
int VideoFrameReceiver::StartCamera() {
	ms_message("%s method:%s: %d", THIS_FILE, __FUNCTION__, __LINE__);
	if (is_camera_started_) {
		// 重置相机
		StopCamera();
	}
	if (camera_input_ == nullptr) {
		CameraInputCreate();
	}
	if (preview_output_ == nullptr) {
		PreviewOutputCreate();
	}
	if (capture_session_ == nullptr) {
		CaptureSessionSetting();
	}
	Camera_ErrorCode ret = OH_CaptureSession_Start(capture_session_);
	if (ret != CAMERA_OK) {
		return ret;
	}
	ms_message("%s method:%s: %d", THIS_FILE, __FUNCTION__, __LINE__);
	is_camera_started_ = true;
	return ret;
}

void VideoFrameReceiver::StopCamera() {
	ms_message("%s method:%s: %d", THIS_FILE, __FUNCTION__, __LINE__);
	OH_CaptureSession_Stop(capture_session_);
}

void VideoFrameReceiver::destroy() {
	ms_message("%s method:%s: %d", THIS_FILE, __FUNCTION__, __LINE__);
	StopCamera();
	if (camera_input_ != nullptr) {
		OH_CameraInput_Close(camera_input_);
		OH_CameraInput_Release(camera_input_);
	}
	if (preview_output_ != nullptr) {
		OH_PreviewOutput_Stop(preview_output_);
		OH_PreviewOutput_Release(preview_output_);
	}
	if (receiver_) {
		OH_ImageReceiverNative_Off(receiver_);
		OH_ImageReceiverNative_Release(receiver_);
		receiverMap_.erase(receiver_);
	}
	if (preview_profile_) {
		OH_PreviewOutput_DeleteProfile(preview_profile_);
	}
	if (capture_session_) {
		OH_CaptureSession_Release(capture_session_);
	}
	if (camera_manager_) {
		OH_Camera_DeleteCameraManager(camera_manager_);
	}
	steam = nullptr;
	preview_output_ = nullptr;
	camera_input_ = nullptr;
	capture_session_ = nullptr;
	is_camera_started_ = false;
	preview_profile_ = nullptr;
	camera_manager_ = nullptr;
	cameras_ = nullptr;
	receiver_ = nullptr;
}

Camera_Device VideoFrameReceiver::getCamera(int index) {
	if (index < 0) {
		index = camera_dev_index_;
	}
	return cameras_[index];
}
Camera_Profile **VideoFrameReceiver::getPreviewProfileByIndex(int index, uint32_t *size) {
	Camera_OutputCapability *cameraOutputCapability = nullptr;
	Camera_ErrorCode ret =
	    OH_CameraManager_GetSupportedCameraOutputCapability(camera_manager_, &cameras_[index], &cameraOutputCapability);
	if (cameraOutputCapability == nullptr || ret != CAMERA_OK) {
		ms_message("%s method:%s: %d", THIS_FILE, __FUNCTION__, __LINE__);
		return nullptr;
	}
	ms_message("%s method:%s: %d", THIS_FILE, __FUNCTION__, __LINE__);
	if (cameraOutputCapability->metadataProfilesSize < 0) {
		return nullptr;
	}
	ms_message("%s method:%s: %d", THIS_FILE, __FUNCTION__, __LINE__);
	int count = 0;
	for (int i = 0; i < cameraOutputCapability->previewProfilesSize; i++) {
		preview_profile_ = cameraOutputCapability->previewProfiles[i];
		if (preview_profile_->size.width == width_ && preview_profile_->size.height == height_) {
			profile_index_ = i;
		}
		count++;
	}
	if (size) {
		*size = count;
	}
	ms_message("%s method:%s: %d", THIS_FILE, __FUNCTION__, __LINE__);
	preview_profile_ = cameraOutputCapability->previewProfiles[profile_index_];
	return cameraOutputCapability->previewProfiles;
}
void VideoFrameReceiver::PreviewOutputCreate() {
	getPreviewProfileByIndex(camera_dev_index_, nullptr);
	auto surfaceId = std::to_string(surfaceId_);
	ms_message("%s method:%s: %d", THIS_FILE, __FUNCTION__, __LINE__);
	Camera_ErrorCode ret =
	    OH_CameraManager_CreatePreviewOutput(camera_manager_, preview_profile_, surfaceId.c_str(), &preview_output_);
	if (preview_profile_ == nullptr || preview_output_ == nullptr || ret != CAMERA_OK) {
		ms_message("%s method:%s: %d", THIS_FILE, __FUNCTION__, __LINE__);
	}
}

void VideoFrameReceiver::CameraInputCreate() {
	ms_message("%s method:%s: %d", THIS_FILE, __FUNCTION__, __LINE__);
	Camera_ErrorCode ret =
	    OH_CameraManager_CreateCameraInput(camera_manager_, &cameras_[camera_dev_index_], &camera_input_);
	if (camera_input_ == nullptr || ret != CAMERA_OK) {
		ms_message("%s method:%s: %d", THIS_FILE, __FUNCTION__, __LINE__);
	}
	ret = OH_CameraInput_Open(camera_input_);
	if (ret != CAMERA_OK) {
		ms_message("%s method:%s: %d", THIS_FILE, __FUNCTION__, __LINE__);
	}
}

void VideoFrameReceiver::CaptureSessionSetting() {
	Camera_ErrorCode ret;
	ret = OH_CameraManager_CreateCaptureSession(camera_manager_, &capture_session_);
	if (capture_session_ == nullptr || ret != CAMERA_OK) {
		return;
	}
	ret = OH_CaptureSession_SetSessionMode(capture_session_, NORMAL_VIDEO);
	if (ret != CAMERA_OK) {
	}
	ret = OH_CaptureSession_BeginConfig(capture_session_);
	if (ret != CAMERA_OK) {
	}
	if (camera_input_) {
		ret = OH_CaptureSession_AddInput(capture_session_, camera_input_);
		if (ret != CAMERA_OK) {
			return;
		}
	}
	if (preview_output_) {
		ret = OH_CaptureSession_AddPreviewOutput(capture_session_, preview_output_);
		if (ret != CAMERA_OK) {
			return;
		}
	}
	ret = OH_CaptureSession_CommitConfig(capture_session_);
	if (ret != CAMERA_OK) {
		return;
	}
	ms_message("%s method:%s: %d", THIS_FILE, __FUNCTION__, __LINE__);
}